<template>
  <div class="my-tinymce">
    <div class="hover-line" @mousedown="mouseEnter" />
    <Editor :id="editorId" v-model="contentValue" :init="myInit" :disabled="disabled" />
  </div>
</template>

<script>
import { onMounted, onUnmounted, reactive, toRefs, watch } from 'vue'
import { upload } from 'api/common'
// 引入tinymce编辑器
import Editor from '@tinymce/tinymce-vue'
import tinymce from 'tinymce/tinymce' // tinymce默认hidden，不引入则不显示编辑器
// 导入配置文件
import './js/importTinymce'
import { init } from './js/config'
import { ElMessage } from 'element-plus'
import { getFileSize } from '@/util'

export default {
  name: 'MyEditor',
  components: {
    Editor
  },
  props: {
    // 绑定文本值
    modelValue: {
      type: String,
      default: ''
    },
    editorId: {
      type: String,
      default: 'tinymce'
    },
    // placeholder
    placeholder: {
      type: String,
      default: '请输入内容'
    },
    // 默认样式
    style: {
      type: Object,
      default: () => {
        return { width: '100%', height: '200px' }
      }
    },
    // 图片上传服务器地址
    imgUploadUrl: {
      type: String,
      default: ''
    },
    disabled: {
      type: Boolean,
      default: false
    },
    atList: {
      type: Array,
      default() {
        return []
      }
    }
  },
  emits: ['update:modelValue', 'enter', 'delete', 'updateAtList'],
  setup(props, { emit }) {
    const state = reactive({
      myInit: customer(init), // 初始化
      contentValue: props.modelValue, // 绑定文本
      timeout: null
    })

    const enterDisabled = ref(false)
    const editor = ref(null)
    const mouseType = ref('')
    const currentY = ref(0)
    const currentHeight = ref(140)

    onMounted(() => {
      tinymce.init({
        selector: `#${props.editorId}`
      })
    })

    onUnmounted(() => {
      document.body.removeEventListener('mousemove', mouseMove)
      document.body.removeEventListener('mouseup', mouseUp)
    })

    // 侦听文本变化并传给外界
    watch(() => state.contentValue, n => {
      emit('update:modelValue', n)
    })
    // 侦听默认值 外界第一次传进来一个 v-model 就赋值给 contentValue
    watch(() => props.modelValue, n => {
      if (n !== state.contentValue) {
        state.contentValue = n
      }
    })

    function mouseEnter(e) {
      currentY.value = e.pageY
      mouseType.value = 'Enter'
      document.body.addEventListener('mousemove', mouseMove)
      document.body.addEventListener('mouseup', mouseUp)
      document.body.style = 'user-select: none'
    }

    function mouseUp() {
      mouseType.value = ''
      document.body.removeEventListener('mousemove', mouseMove)
      document.body.removeEventListener('mouseup', mouseUp)
      document.body.style = ''
    }

    // console.log(document.querySelector('.mouse-move-wrap'))
    // document.querySelector('.mouse-move-wrap').addEventListener('mousemove', mouseMove)

    function mouseMove(e) {
      if (mouseType.value === 'Enter') {
        // console.log(e.pageY - currentY.value)
        currentHeight.value += currentY.value - e.pageY
        currentY.value = e.pageY
        // console.log(currentHeight)
        if (currentHeight.value <= 140 || currentHeight.value >= 500) {
          return
        }
        if (document.querySelector('.list-wrap').scrollTop >= document.querySelector('.list-wrap .list').clientHeight - document.querySelector('.chat-wrap').clientHeight - 30) {
          document.querySelector('.list-wrap').scrollTop = document.querySelector('.list').clientHeight
        }
        editor.value.dom.setStyle(editor.value.editorContainer, 'height', currentHeight.value + 'px')
      }
    }

    function debounce(fn, wait = 400)  {
      // console.log('进到了防抖', wait)
      if (state.timeout !== null) {
        clearTimeout(state.timeout)
      }
      state.timeout = setTimeout(fn, wait)
    }

    // 参数自定义初始化
    function customer(init) {
      // 允许外界传进来高度和placeholder
      init.height = props.style.height
      init.placeholder = props.placeholder

      init.cfyun_atwho = true
      init.atwho_query_all = true
      init.atwho_query_callback = (name, callback) => {
        return callback(props.atList)
      }
      init.atwho_data_callback = data => {
        emit('updateAtList', data)
      }

      // 粘贴图片 自动处理 base64
      init.urlconverter_callback = (url, node) => {
        if (node === 'img' && url.startsWith('blob:')) {
          tinymce.activeEditor && tinymce.activeEditor.uploadImages()
        }
        return url
      }
      // 图片上传
      init.images_upload_handler = (blobInfo, success, failure) => {
        imgUploadFn(blobInfo, success, failure)
      }

      init.setup = ed => {
        ed.ui.registry.addButton('customInsertButton', {
          icon: 'screenshot',
          tooltip: '插入文件',
          onAction: function(_) {
            var input = document.createElement('input')
            input.setAttribute('type', 'file')
            // 你可以给input加accept属性来限制上传的文件类型
            // 例如：input.setAttribute('accept', '.jpg,.png')
            const acceptList = ['.jpg', '.jpeg', '.png', '.gif', '.doc', '.docx', '.ppt', '.pptx', '.pdf', '.xlsx', '.xls', '.txt', '.zip', '.rar', '.mp3', '.mp4']
            input.setAttribute('accept', acceptList.join(','))
            input.click()
            input.onchange = function() {
              var file = this.files[0]
              const afterFix = file.name.substring(file.name.lastIndexOf('.'))
              if (file.size / 1024 / 1024 > 20) {
                ElMessage.error('上传失败，文件大小请控制在 20M 以内')
                return
              } else if (!acceptList.includes(afterFix)) {
                ElMessage.error('请选择正确的文件类型')
                return
              }
              const formData = new FormData()
              formData.append('file', file)
              upload(formData).then(res => {
                if (res.success) {
                  const previewArr = ['.pdf', '.mp3', '.mp4']
                  const imgArr = ['.jpg', '.jpeg', '.png', '.gif']
                  if (imgArr.includes(afterFix)) {
                    ed.insertContent(`<img src="${res.result.attachmentPath}" alt="" data-mce-src="${res.result.attachmentPath}">`)
                  } else if (previewArr.includes(afterFix)) {
                    ed.insertContent(`<a class="editor-download-file-flag color-primary hand" target="_blank" href="${res.result.attachmentPath}" data-attach="${res.result.attachmentId}" data-download="${file.name}">${file.name} (${getFileSize(file.size)})</a>&nbsp;`)
                  } else {
                    ed.insertContent(`<span class="editor-download-file-flag color-primary hand" style="text-decoration: underline" data-attach="${res.result.attachmentId}" data-download="${file.name}">${file.name} (${getFileSize(file.size)})</span>&nbsp;`)
                  }
                } else {
                  // failure('上传出错，服务器开小差了呢')
                }
              }).catch(() => {
                // failure('上传出错，服务器开小差了呢')
              })
            }
          }
        })
        ed.on('keyup', e => {
          if (e.code.match('Shift') || e.code.match('Control')) {
            enterDisabled.value = false
          }
          if ((e.code === 'Backspace' || e.code === 'Delete')) {
            emit('update:modelValue', editor.value.getContent())
            emit('delete')
          }
        })
        ed.on('keydown', e => {
          if (e.code.match('Shift') || e.code.match('Control')) {
            enterDisabled.value = true
          }
          if (e.code === 'Tab') {
            e.preventDefault()
            setTimeout(() => {
              emit('update:modelValue', editor.value.getContent())
            }, 100)
          }
          if ((e.code === 'Enter' || e.code === 'NumpadEnter') && !enterDisabled.value) {
            e.preventDefault()
            emit('enter')
          }
        })
        ed.on('init', function(ed) {
          ed.target.editorCommands.execCommand('fontSize', false, '14px')
          ed.target.editorCommands.execCommand('fontColor', false, '#181818')
        })
        ed.on('mouseUp', function() {
          mouseUp()
        })

        editor.value = ed
      }

      return init
    }

    async function imgUploadFn(blobInfo, success, failure) {
      // 可以限制图片大小
      if (blobInfo.blob().size / 1024 / 1024 > 2) {
        failure('上传失败，图片大小请控制在 2M 以内')
      } else {
        const params = new FormData()
        params.append('file', dataURLtoFile('data:image/jpeg;base64,' + blobInfo.base64(), blobInfo.filename()))
        // 图片上传
        upload(params).then(res => {
          if (res.success) {
            success(res.result.attachmentPath)
          } else {
            failure('上传出错，服务器开小差了呢')
          }
        }).catch(() => {
          failure('上传出错，服务器开小差了呢')
        })
      }
    }

    function dataURLtoFile(dataUrl, filename) { // 将base64转换为文件，dataUrl为base64字符串，filename为文件名（必须带后缀名，如.jpg,.png）
      const arr = dataUrl.split(','); const mime = arr[0].match(/:(.*?);/)[1]
      const str = window.atob(arr[1]); let n = str.length; const u8arr = new Uint8Array(n)
      while (n--) {
        u8arr[n] = str.charCodeAt(n)
      }
      return new File([u8arr], filename, { type: mime })
    }

    return ({
      ...toRefs(state),
      customer,
      debounce,
      editor,
      mouseMove,
      mouseType,
      mouseEnter,
      currentY,
      currentHeight,
      mouseUp
    })
  }
}
</script>

<style lang="scss">
.my-tinymce {
  position: relative;
  .hover-line {
    position: absolute;
    top: -10px;
    left: 0;
    height: 12px;
    z-index: 102;
    width: 100%;
    cursor: n-resize;
  }
  .tox-editor-header {
    height: 32px;
    .tox-tbtn {
      height: 32px;
      margin-top: 0;
      margin-bottom: 0;
    }
  }
  .tox-tinymce {
    border: 1px solid #dfdede;
    border-radius: 6px;
    z-index: 100 !important;
  }
  .tox-toolbar__primary {
    border-bottom: 1px solid #dfdede !important;
    background-image: none !important;
  }
  .tox-statusbar {
    border-top: none !important;
    height: 34px !important;
  }
  .tox {
    svg {
      fill: #8a8e96 !important;
    }
  }
}
.tox .tox-dialog-wrap {
  .tox-dialog {
    border-radius: 6px;
    border: none;
    .tox-lock {
      svg {
        fill: $--color-desc !important;
      }
    }
    .tox-form {
      padding-right: 0;
      .tox-form__group {
        margin-bottom: 0;
        .tox-label {
          color: #333;
          margin-bottom: 6px;
          font-size: 12px;
        }
      }
      input {
        transition: all linear 0.1s;
        border-radius: 2px;
        border-color: #dcdfe6;
        &:focus {
          border-color: $--color-primary;
        }
      }
    }
    .tox-dialog__body {
      display: flex;
      flex-direction: column;
      padding: 10px 4px;
      .tox-dialog__body-nav {
        display: none;
        flex-direction: row;
        align-items: center;
        .tox-dialog__body-nav-item {
          margin-right: 20px;
          font-size: 14px;
          margin-bottom: 0;
          padding-bottom: 4px;
        }
      }
      .tox-dropzone {
        height: 200px;
      }
      .tox-dialog__body-content {
        padding: 10px 20px;
      }
    }
  }
  .tox-dialog__header {
    border-bottom: 1px solid #ebeef5;
    margin-right: 0;
    padding: 12px 20px;
    .tox-dialog__title {
      display: flex;
      align-items: center;
      font-weight: 700;
      white-space: nowrap;
      flex-shrink: 0;
      font-size: $--font-size-large;
      line-height: 1.5;
      &::before {
        content: "";
        width: 4px;
        height: 12px;
        background: $--color-primary;
        border-radius: 0 2px 2px 0;
        margin-right: 10px;
      }
    }
    .tox-button {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 14px;
      height: 14px;
      &:hover {
        background: #fff;
        border: none;
      }
      .tox-icon {
        svg {
          fill: var(--el-color-info);
        }
      }
    }
  }
  .tox-dialog__body-nav {
    padding-right: 0;
    .tox-dialog__body-nav-item {
      margin-bottom: 15px;
      transition: all linear 0.1s;
      &:focus {
        background: none;
      }
      &.tox-dialog__body-nav-item--active {
        color: $--color-primary;
        border-color: $--color-primary;
        background: none;
      }
    }
  }
  .tox-dialog__footer {
    border-top: 1px solid #ebeef5;
    padding-bottom: 12px;
    padding-top: 12px;
    .tox-button {
      background: $--color-primary;
      border-color: $--color-primary;
      color: $--color-whites;
      &:hover,
      &:focus,
      &:active {
        border-color: #66a5fd;
        background-color: #66a5fd;
        outline: none;
      }
    }
  }
  .tox-button {
    font-weight: 400;
    padding: 8px;
    line-height: 1;
    border-radius: 2px;
  }
  .tox-button.tox-button--secondary {
    color: #999;
    background: #f5f7fa;
    border: 1px solid #f5f7fa;
    &:hover,
    &:focus,
    &:active {
      border-color: #e9f2ff;
      color: $--color-primary;
      background-color: #e9f2ff;
      outline: none;
    }
  }
}
.tox-tinymce-aux {
  z-index: 350000 !important;
}
</style>
